package elastic

import (
	"context"
	"github.com/goccy/go-json"
	"github.com/olivere/elastic/v7"
	"strings"
	"time"
	"yxs-mall/app/mall/pojo/bo"
	"yxs-mall/app/mall/pojo/searchcond"
	"yxs-mall/common/query"
	"yxs-mall/common/storage"
)

type ProductSearchModel struct {
	_class string
	Id     int64
	/**
	 * 用户id
	 */
	UserId int64

	/**
	 * 标题
	 */
	Title string
	/**
	 * 封面
	 */
	Cover string

	/**
	 * 标签
	 */
	Tag string

	Attrs []Attr

	/**
	 * 商品类型1文档，2实体
	 */
	Type int

	/**
	 * 商品描述
	 */
	Description string

	/**
	 * 热度
	 */
	Hot int

	/**
	 * 价格类型1:免费，2:收费，3:会员
	 */
	PriceType int

	/**
	 * 价格
	 */
	Price float64

	/**
	 * 上架状态:1->未上架，2->上架，3->上架中
	 */
	PuplishState int

	/**
	 * 文件类型 1-pdf, 2-image, 3-其它
	 */
	FileType int

	/**
	 * 上架时间
	 */
	PuplishTime time.Time

	CreateTime time.Time
}

type Attr struct {
	AttrId int

	AttrName string

	AttrValue string

	Sort int
}

const productMapping = `
{
	"mappings": {
		"properties": {
		"_class": {
		  "type": "text",
		  "fields": {
			"keyword": {
			  "type": "keyword",
			  "ignore_above": 256
			}
		  }
		},
		"attrs": {
		  "type": "nested",
		  "properties": {
			"attrId": {
			  "type": "long"
			},
			"attrName": {
			  "type": "text",
		  "analyzer": "ik_max_word",
		  "search_analyzer": "ik_max_word",
			  "fields": {
				"keyword": {
				  "type": "keyword",
				  "index": false,
				  "doc_values": false
				}
			  }
			}
		  }
		},
		"cover": {
		  "type": "text",
		  "fields": {
			"keyword": {
			  "type": "keyword",
			  "ignore_above": 256
			}
		  }
		},
		"createTime": {
		  "type": "date"
		},
		"description": {
		  "type": "text",
		  "analyzer": "ik_max_word",
		  "search_analyzer": "ik_max_word",
		  "fields": {
			"keyword": {
			  "type": "keyword",
			  "ignore_above": 256
			}
		  }
		},
		"fileType": {
		  "type": "long"
		},
		"id": {
		  "type": "long"
		},
		"price": {
		  "type": "float"
		},
		"priceType": {
		  "type": "long"
		},
		"puplishState": {
		  "type": "long"
		},
		"puplishTime": {
		  "type": "date"
		},
		"tag": {
		  "type": "text",
		  "analyzer": "ik_max_word",
		  "search_analyzer": "ik_max_word",
		  "fields": {
			"keyword": {
			  "type": "keyword",
			  "ignore_above": 256
			}
		  }
		},
		"title": {
		  "type": "text",
		  "analyzer": "ik_max_word",
		  "search_analyzer": "ik_max_word",
		  "fields": {
			"keyword": {
			  "type": "keyword",
			  "ignore_above": 256
			}
		  }
		},
		"type": {
		  "type": "long"
		},
		"userId": {
		  "type": "long"
		}
	}
}
`

var client = storage.ElasticClient

var esCtx = context.Background()

const productIndex = "mall_product"

type productElastic struct {
}

var ProductElastic = productElastic{}

func (e productElastic) CreateIndex() error {
	exist, err := client.IndexExists(productIndex).Do(esCtx)
	if err != nil {
		return err
	}
	if !exist {
		return nil
	}
	// 创建索引
	_, err = client.CreateIndex(productIndex).BodyString(productMapping).Do(esCtx)
	if err != nil {
		return err
	}
	return nil
}

func (e *productElastic) Search(cond *searchcond.ProductSearchCond) (*query.SearchResult[bo.ProductDocumentDetailBo], error) {
	client = storage.ElasticClient
	result := new(query.SearchResult[bo.ProductDocumentDetailBo])
	service := client.Search().Index(productIndex)
	buildSearch(service, cond)
	list, err := service.Pretty(true).Do(context.Background())
	if err != nil {
		return result, err
	}

	for _, item := range list.Hits.Hits {
		productBo := new(bo.ProductDocumentDetailBo)
		bytes, _ := item.Source.MarshalJSON()
		err := json.Unmarshal(bytes, productBo)
		if err != nil {
			return nil, err
		}
		result.Hits = append(result.Hits, *productBo)
	}
	return result, nil
}

func buildSearch(service *elastic.SearchService, cond *searchcond.ProductSearchCond) {
	newBoolQuery := elastic.NewBoolQuery()
	newBoolQuery.Filter(elastic.NewTermQuery("puplishState", 3))
	if cond.Keyword != "" {
		newBoolQuery.Must(elastic.NewMultiMatchQuery(cond.Keyword, "title", "tag"))
	}
	if cond.LimitUserId != 0 {
		newBoolQuery.Must(elastic.NewTermQuery("userId", cond.LimitUserId))
	}
	if cond.PriceType != 0 {
		newBoolQuery.Filter(elastic.NewTermQuery("priceType", cond.PriceType))
	}

	if cond.Attrs != nil && len(cond.Attrs) > 0 {
		for _, m := range cond.Attrs {
			boolQuery := newBoolQuery.Filter(elastic.NewTermQuery("attrs.attrId", m.AttrId))
			newBoolQuery.Filter(elastic.NewNestedQuery("attrs", boolQuery))
		}
	}

	if cond.Price != "" {
		priceRange := elastic.NewRangeQuery("price")
		prices := strings.Split(cond.Price, "_")
		if len(prices) == 2 {
			if prices[0] != "" && prices[1] != "" {
				priceRange.Gte(prices[0]).Lte(prices[1])
			} else if prices[0] == "" && prices[1] != "" {
				priceRange.Lte(prices[1])
			} else if prices[0] != "" && prices[1] == "" {
				priceRange.Gte(prices[0])
			}
		}
		service.Query(priceRange)
	}

	if cond.Keyword != "" {
		highlight := elastic.NewHighlight()
		highlight.Field("title").Field("tag")
		highlight.PreTags("<b style='color:#e4393c'>").PostTags("</b>")
		service.Highlight(highlight)
	}

	setOrderSort(service, cond)
	setPage(service, cond)

	service.Query(newBoolQuery)
}

func setOrderSort(service *elastic.SearchService, cond *searchcond.ProductSearchCond) {
	sorts := cond.GetOrderSort()
	for _, item := range sorts {
		service.Sort(item.SortField, item.SortType)
	}
	service.Sort("_score", false)
}

func setPage(service *elastic.SearchService, cond *searchcond.ProductSearchCond) {
	pageNum := cond.PageNum
	if pageNum > 0 {
		pageNum = pageNum - 1
	}
	service.From(pageNum * cond.PageSize).Size(cond.PageSize)
}
